#include "netchannel.h"
#include <QDir>
#include "Net/MyRSA.h"
#include "Msg/Req/reqhead.h"
#include "Main/global.h"
#include "zlib.h"

const char* THREE_DES_KEY = "240262447423713749922240";

AuthChannel::AuthChannel()
{
    qDebug()<<"currentPath:"<<QDir::currentPath();
    m_sPriKeyPath += QDir::currentPath().toStdString() + "/cert/";
    m_sPriKeyPath += "client.pfx";

    m_sPriKeyPwd = "123456";
    m_sPubKeyPath += QDir::currentPath().toStdString()  + "/cert/";
    m_sPubKeyPath += "server.crt";
}

int AuthChannel::SufRecvHandle(const char *pRecvBuf, unsigned int ulLen, std::string &sRsp)
{
    X509 * x509Pfx = 0;
    EVP_PKEY * pPubKeyPfx = 0;
    EVP_PKEY * pPriKeyPfx = 0;

    FILE *fp = 0;
    PKCS12 *p12 = 0;


    /// 读入P12
    if (!(fp = fopen (m_sPriKeyPath.c_str(), "rb")))
        return -1;

    SSLeay_add_all_algorithms();
    p12 = d2i_PKCS12_fp (fp, 0);
    fclose(fp);
    if (!(p12))
        return -1;

    int nRtn = PKCS12_parse(p12, m_sPriKeyPwd.c_str(), &pPriKeyPfx, 0, 0);
    if (nRtn != 1)
    {
        PKCS12_free(p12);
        return -1;
    }

    PKCS12_free(p12);
    if (pPriKeyPfx == 0)
    {
        return -1;
    }
    else if (RSA_size(pPriKeyPfx->pkey.rsa) == 0)
    {
        return -1;
    }

    int nInLen = 0;
    unsigned int nOutLen = 0;
    std::shared_ptr<unsigned char> pDecrpted(new unsigned char[RSA_size(pPriKeyPfx->pkey.rsa)], std::default_delete<unsigned char[]>());

    std::shared_ptr<unsigned char> pDecryptedTotal(new unsigned char[ulLen+128], std::default_delete<unsigned char[]>());
    memset(pDecryptedTotal.get(), 0, ulLen+128);
    //memcpy(pDecryptedTotal,szRecvBuf,MSGLEN);

    const char * pData = pRecvBuf + MSGLEN;
    unsigned int uiBodyLen = ulLen - MSGLEN;
    bool bOK = true;
    for (int m = 128; m - 128 < uiBodyLen; m+=128)
    {
        nInLen = uiBodyLen / m == 0 ? uiBodyLen - (m - 128) : 128;
        int nTmp = RSA_private_decrypt(nInLen,(const unsigned char*)pData,pDecrpted.get(),pPriKeyPfx->pkey.rsa,RSA_PKCS1_PADDING);
        if (nTmp > 0)
        {
            memcpy(pDecryptedTotal.get() + MSGLEN + nOutLen,pDecrpted.get(),nTmp);
            nOutLen += nTmp;
            pData += nInLen;
        }
        else
        {
            bOK = false;
            break;
        }
    }

    if (bOK)
    {
        memcpy(pDecryptedTotal.get(),Get8BitLength(nOutLen).c_str(),MSGLEN);

        QString str = QString::fromUtf8((const char*)pDecryptedTotal.get());
        //        qDebug()<<"fromUtf8:"<<str;
        qDebug()<<"fromLocal8Bit:"<<QString::fromLocal8Bit((const char*)pDecryptedTotal.get());
        sRsp.assign((const char*)pDecryptedTotal.get(), MSGLEN + nOutLen);
    }

    return 1;
}

int AuthChannel::PreSendHandle(std::shared_ptr<char> &sendEnBuf, const std::string sSend)
{
    unsigned int uiTotalLen = sSend.length();
    //    const char * pData = sSend.c_str() + MSGLEN;
    //    unsigned int uiLen = uiTotalLen - MSGLEN;
    char *pEncryptedPart = NULL;
    unsigned int nOutLen = 0;
    CMyRSA::HandleRsaEncrypt(pEncryptedPart, nOutLen, sSend.c_str(), uiTotalLen, m_sPubKeyPath.c_str());

    // 分配加上长度部分的内存
    sendEnBuf = std::shared_ptr<char>(new char[nOutLen+MSGLEN],std::default_delete<char[]>());
    //    char *pEncryptedTotal = new char[nOutLen+MSGLEN];
    memset(sendEnBuf.get(),0,nOutLen+MSGLEN);

    memcpy(sendEnBuf.get(), Get8BitLength(nOutLen).c_str(),MSGLEN);

    // 复制加密后的报文
    memcpy(sendEnBuf.get()+MSGLEN, pEncryptedPart, nOutLen);
    return MSGLEN + nOutLen;
}

TradeChannel::TradeChannel()
{
    //    m_s3DesKey = "240262447423713749922240";
    m_s3DesIV = "12345678";
//    m_sSessionID = "12345678";
}

int TradeChannel::SufRecvHandle(const char *pRecvBuf, unsigned int ulLen, string &sRsp)
{
    char cFlag = *(pRecvBuf + MSGLEN);
    if (cFlag == 0x01)
    {
        return Unzip(pRecvBuf,ulLen,sRsp);
    }
    else if (cFlag == 0x02 || cFlag == 0x03)
    {
        return Decrypt(pRecvBuf,ulLen,sRsp,cFlag);
    }
    else
    {
        return 0;
    }
}

int TradeChannel::PreSendHandle(std::shared_ptr<char> &sendEnBuf, const string sSend)
{
//    if (m_sSessionID.empty())
//    {
//        m_sSessionID = Global::Instance()->m_qsSessionID.toStdString();
//    }
//    else
//        m_sSessionID = "1100002894";

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);
    EVP_EncryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, (const unsigned char*)THREE_DES_KEY, (const unsigned char*)m_s3DesIV.c_str());

    unsigned int uiHeadLen = MSGLEN + 1 + m_sSessionID.length();
    //    unsigned char* pEccrypted = new unsigned char[uiHeadLen + uiLen + 128];

    std::shared_ptr<unsigned char> pDecrypted(new unsigned char[uiHeadLen + sSend.length() + 128], std::default_delete<unsigned char[]>());
    int nEncryptedLen = 0;
    int nRtn = 0;
    do
    {
        nRtn = EVP_EncryptUpdate(&ctx, pDecrypted.get() + uiHeadLen,&nEncryptedLen,(const unsigned char*)sSend.c_str(),sSend.length());
    } while(false);

    int nTmpLen = 0;
    nRtn = EVP_EncryptFinal_ex(&ctx, pDecrypted.get() + uiHeadLen + nEncryptedLen, &nTmpLen);
    nEncryptedLen += nTmpLen;

    char szHeader[128] = {0};
    sprintf(szHeader,"%08d%d%s", 1 + m_sSessionID.length() + nEncryptedLen,0x02,m_sSessionID.c_str());
    szHeader[8] = 0x02;

    memcpy(pDecrypted.get(),szHeader,uiHeadLen);
    qDebug()<<"szHeader:"<<szHeader<<" uiHeadLen:"<<uiHeadLen;
    //    int nSendLen = SendData((const char*)pEccrypted,nEncryptedLen + uiHeadLen);
    //    delete []pEccrypted;

    sendEnBuf = std::shared_ptr<char>(new char[nEncryptedLen + uiHeadLen],std::default_delete<char[]>());
    memcpy(sendEnBuf.get(), pDecrypted.get(), nEncryptedLen+uiHeadLen);
    qDebug()<<"session:"<<m_sSessionID.c_str()<<" encyrp len:"<< nEncryptedLen+uiHeadLen;
    return nEncryptedLen + uiHeadLen;
}

void TradeChannel::SetSession(string sSessionID)
{
    m_sSessionID = sSessionID;
}

const int MaxZipPercent = 50;
int TradeChannel::Unzip(const char *pRecvBuf, unsigned int ulLen, string &sRsp)
{
    int nFlagLen = 1;
    int nFlag = static_cast<int>(*(pRecvBuf + MSGLEN + nFlagLen));
    int nHeadLen = MSGLEN + nFlagLen;

    unsigned int uiBufLen = ulLen*MaxZipPercent;
    //    qDebug()<<"Error,不支持压缩";
    //    return -1;
    char *pPlainBuff = new char[uiBufLen];
    std::shared_ptr<char> sp(new char[uiBufLen], std::default_delete<char[]>());
    z_stream stream;
    stream.zalloc = 0;
    stream.zfree = 0;
    stream.opaque = 0;
    stream.avail_in = ulLen - nHeadLen;
    stream.next_in = (Bytef*)(pRecvBuf + nHeadLen);

    int nRtnZip = 0;
    if (Z_OK != inflateInit2(&stream, 16 + MAX_WBITS))
    {
        return -1;
    }

    stream.avail_out = uiBufLen;
    stream.next_out = (Bytef*)sp.get();
    do
    {
        nRtnZip = inflate(&stream,Z_NO_FLUSH);
        if (Z_OK != nRtnZip && Z_STREAM_END != nRtnZip)
        {
            return -1;
        }
    } while (false);//nRtnZip != Z_STREAM_END);
    inflateEnd(&stream);
    sRsp.assign(sp.get(), stream.total_out);
    return stream.total_out;
}

int TradeChannel::Decrypt(const char *pRecvBuf, unsigned int ulLen, string &sRsp, char cMode)
{
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);

    //    if (cMode == 0x02)
    //    {
    //        string sKey = "240262447423713749922240";
    //        EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, DES_KEY, (const unsigned char*)m_s3DesIV.c_str());
    //    }
    //    else
    {
        EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, (const unsigned char*)THREE_DES_KEY, (const unsigned char*)m_s3DesIV.c_str());
    }

    unsigned int uiHeadLen = MSGLEN + 1 + m_sSessionID.length();
    const char* pPktData = pRecvBuf + uiHeadLen;

    //        unsigned char* pDecrypted = new unsigned char[ulLen - uiHeadLen + 128];
    std::shared_ptr<unsigned char> pDecrypted(new unsigned char[ulLen - uiHeadLen + 128], std::default_delete<unsigned char[]>());
    int nDecryptedLen = 0;
    int nRtn = 0;
    do
    {
        nRtn = EVP_DecryptUpdate(&ctx,pDecrypted.get(),&nDecryptedLen,(const unsigned char*)pPktData,ulLen - uiHeadLen);
    } while(false);

    int nTmpLen = 0;
    nRtn = EVP_DecryptFinal_ex(&ctx, pDecrypted.get() + nDecryptedLen, &nTmpLen);
    nDecryptedLen += nTmpLen;
    sRsp.assign((const char*)pDecrypted.get() , nDecryptedLen);
    return nDecryptedLen;
}
